Skip to content

创建聊天机器人 (ChatInterface)

聊天机器人是大型语言模型 (LLM) 的一个流行应用。通过 Gradio,您可以轻松构建聊天机器人模型的演示并与用户分享,或者使用直观的聊天界面自己试用。

本教程使用 gr.ChatInterface(),这是一个高级抽象,可让您快速创建聊天机器人 UI,通常只需几行代码即可完成。构建的聊天机器人界面包含了现代化的设计,支持消息历史、流式输出和多模态交互等功能。

定义聊天函数

使用 gr.ChatInterface() 创建聊天机器人的第一步是定义您的聊天函数。在最简单的情况下,您的聊天函数应该接受两个参数:messagehistory(参数可以任意命名,但必须按此顺序)。

  • message: 一个 str 字符串,表示用户的最新消息。
  • history: 一个 list,包含 OpenAI 风格的消息字典,每个字典有 rolecontent 键,代表到该点为止的对话历史。

示例 history 可能如下所示:

python
[
    {"role": "user", "content": "法国的首都是什么?"},
    {"role": "assistant", "content": "巴黎"}
]

而下一个 message 可能是:

python
"它最大的城市是什么?"

您的聊天函数应该返回一个字符串响应,这是机器人对特定用户输入 message 的回复,例如:

python
"巴黎也是法国最大的城市。"

基础示例:随机回答"是"或"否"的聊天机器人

让我们编写一个简单的聊天功能,随机响应 "是" 或 "否":

python
import random
import gradio as gr

def random_response(message, history):
    return random.choice(["是", "否"])

demo = gr.ChatInterface(random_response, type="messages")

if __name__ == "__main__":
    demo.launch()

利用用户输入和历史记录的示例

前面的例子非常简单,它甚至没有考虑用户输入或以前的历史记录!这里是另一个简单的例子,展示如何整合用户的输入以及历史记录:

python
import gradio as gr

def alternatingly_agree(message, history):
    # 根据历史长度决定响应
    if len([h for h in history if h['role'] == "assistant"]) % 2 == 0:
        return f"是的,我认为'{message}'是对的"
    else:
        return "我不这么认为"

demo = gr.ChatInterface(alternatingly_agree, type="messages")

if __name__ == "__main__":
    demo.launch()

流式聊天机器人(Streaming)

在您的聊天函数中,您可以使用 yield 来生成一系列部分响应,每个响应都会替换之前的响应。这样,您就能创建一个流式聊天机器人,让用户看到回复的生成过程:

python
import time
import gradio as gr

def slow_echo(message, history):
    for i in range(len(message)):
        time.sleep(0.3)  # 模拟思考时间
        yield "您输入的内容: " + message[:i+1]

demo = gr.ChatInterface(slow_echo, type="messages")

if __name__ == "__main__":
    demo.launch()

在响应流式传输过程中,"提交"按钮将变为"停止"按钮,可用于停止生成器函数。

定制化聊天机器人

自定义聊天界面外观

与 Gradio 的 Interface 类似,gr.ChatInterface 包含许多相同的参数,您可以使用这些参数来自定义聊天机器人的外观:

  • 使用 titledescription 添加标题和描述
  • 使用 themecss 添加主题或自定义 CSS
  • 添加 examples 并启用 cache_examples,让用户更容易尝试
  • 自定义聊天机器人界面中的按钮(submit_btnretry_btnundo_btnclear_btn

以下是使用这些参数的示例:

python
import gradio as gr

def yes_man(message, history):
    if message.endswith("?"):
        return "是的"
    else:
        return "请随时向我提问!"

demo = gr.ChatInterface(
    yes_man,
    type="messages",
    chatbot=gr.Chatbot(height=300),
    textbox=gr.Textbox(placeholder="问我一个是或否的问题", container=False, scale=7),
    title="是先生",
    description="向'是先生'提出任何问题",
    theme="soft",
    examples=["你好", "我酷吗?", "番茄是蔬菜吗?"],
    cache_examples=True,
    retry_btn="重试",
    undo_btn="撤销上一条",
    clear_btn="清空",
)

if __name__ == "__main__":
    demo.launch()

如果您想在用户开始聊天之前显示一个"占位符",可以使用 gr.Chatbotplaceholder 参数,它接受 Markdown 或 HTML:

python
gr.ChatInterface(
    yes_man,
    type="messages",
    chatbot=gr.Chatbot(placeholder="<strong>您的专属'是先生'</strong><br>请随意提问"),
    # 其他参数...
)

占位符在聊天机器人中垂直和水平居中显示。

多模态聊天界面

您可能希望为聊天机器人添加多模态功能,允许用户上传图像或文件并询问相关问题。通过向 gr.ChatInterface 传递参数 multimodal=True 即可实现:

python
import gradio as gr

def count_files(message, history):
    num_files = len(message["files"])
    return f"您上传了 {num_files} 个文件"

demo = gr.ChatInterface(
    fn=count_files, 
    type="messages",
    examples=[{"text": "你好", "files": []}], 
    title="文件计数机器人", 
    multimodal=True
)

if __name__ == "__main__":
    demo.launch()

multimodal=True 时,聊天函数的签名会略有变化:

  • 第一个参数应接受一个由提交的文本和上传的文件组成的字典:{"text": "用户输入", "files": ["文件路径1", "文件路径2", ...]}
  • 同样,您提供的任何示例也应位于此形式的字典中

如果您想定制多模态聊天机器人的文本框,您应该将 gr.MultimodalTextbox 的实例传递给 ChatInterfacetextbox 参数:

python
demo = gr.ChatInterface(
    fn=count_files, 
    type="messages",
    multimodal=True,
    textbox=gr.MultimodalTextbox(
        file_count="multiple",  # 允许多文件上传
        file_types=["image"],   # 仅允许图像
        sources=["upload", "microphone"]  # 启用上传和麦克风
    )
)

添加额外输入组件

您可能希望向聊天机器人添加其他参数,例如系统提示文本框或控制响应令牌数量的滑块。ChatInterface 支持 additional_inputs 参数来添加其他输入组件:

python
import gradio as gr
import time

def echo(message, history, system_prompt, tokens):
    response = f"系统提示: {system_prompt}\n 消息: {message}."
    for i in range(min(len(response), int(tokens))):
        time.sleep(0.05)
        yield response[: i + 1]

demo = gr.ChatInterface(
    echo,
    type="messages",
    additional_inputs=[
        gr.Textbox("您好,我是一个AI助手。", label="系统提示"),
        gr.Slider(10, 100, label="生成的最大字符数"),
    ],
)

if __name__ == "__main__":
    demo.launch()

如果您想要更灵活的布局,可以在 gr.Blocks() 中预先定义组件,然后在 additional_inputs 中使用它们:

python
import gradio as gr
import time

def echo(message, history, system_prompt, tokens):
    response = f"系统提示: {system_prompt}\n 消息: {message}."
    for i in range(min(len(response), int(tokens))):
        time.sleep(0.05)
        yield response[: i+1]

with gr.Blocks() as demo:
    system_prompt = gr.Textbox("您好,我是一个AI助手。", label="系统提示")
    slider = gr.Slider(10, 100, label="生成的最大字符数", render=False)

    gr.ChatInterface(
        echo, 
        type="messages",
        additional_inputs=[system_prompt, slider]
    )

if __name__ == "__main__":
    demo.launch()

返回复杂响应

除了返回简单的文本字符串外,您还可以返回更复杂的响应:

返回文件或 Gradio 组件

以下 Gradio 组件可以在聊天界面中显示:

  • gr.Image - 图像
  • gr.Plot - 绘图
  • gr.Audio - 音频
  • gr.HTML - HTML
  • gr.Video - 视频
  • gr.Gallery - 图片集
  • gr.File - 文件

只需从您的函数中返回这些组件之一即可:

python
import gradio as gr

def music(message, history):
    if message.strip():
        return gr.Audio("https://github.com/gradio-app/gradio/raw/main/test/test_files/audio_sample.wav")
    else:
        return "请提供艺术家的名字"

demo = gr.ChatInterface(
    music,
    type="messages",
    textbox=gr.Textbox(placeholder="您想听哪位艺术家的音乐?", scale=7),
)

if __name__ == "__main__":
    demo.launch()

返回多条消息

您可以通过返回消息列表来一次发送多条消息,例如一条文本消息加一个文件:

python
import gradio as gr

def echo_multimodal(message, history):
    response = []
    response.append(f"您写道: '{message['text']}' 并上传了:")
    if message.get("files"):
        for file in message["files"]:
            response.append(gr.File(value=file))
    return response

demo = gr.ChatInterface(
    echo_multimodal,
    type="messages",
    multimodal=True,
    textbox=gr.MultimodalTextbox(file_count="multiple"),
)

if __name__ == "__main__":
    demo.launch()

用户反馈和历史记录

收集用户反馈

要收集用户对聊天模型的反馈,设置 gr.ChatInterface(flagging_mode="manual"),用户就可以对助手的响应进行点赞或点踩。每个被标记的响应以及整个聊天历史都会保存在一个 CSV 文件中。

您也可以通过 flagging_options 参数自定义反馈选项。默认选项是"Like"和"Dislike",将显示为点赞和点踩图标:

python
import gradio as gr
import time

def slow_echo(message, history):
    for i in range(len(message)):
        time.sleep(0.05)
        yield "您输入的内容: " + message[: i + 1]

demo = gr.ChatInterface(
    slow_echo,
    type="messages",
    flagging_mode="manual",
    flagging_options=["喜欢", "垃圾信息", "不适当", "其他"],
    save_history=True,
)

if __name__ == "__main__":
    demo.launch()

启用聊天历史保存

通过设置 gr.ChatInterface(save_history=True) 可以启用持久聊天历史记录,让用户保持多个对话并轻松切换。启用后,对话将存储在用户浏览器的本地存储中,以侧面板形式显示之前的对话。

这意味着多个用户可以同时与同一个 ChatInterface 交互,同时保持自己的私人对话历史。

通过 API 使用聊天机器人

构建 Gradio 聊天机器人并将其托管在 Hugging Face Spaces 或其他地方后,您可以使用 /chat 端点上的简单 API 对其进行查询。端点只需要用户的消息(如果您使用 additional_inputs 参数设置了任何内容,则可能需要其他输入),并将返回响应,内部跟踪迄今为止发送的消息。

要使用端点,您应该使用 Gradio Python 客户端或 Gradio JS 客户端。您也可以将 Chat Interface 部署为 Discord 机器人、Slack 机器人或网站小部件。

与实际语言模型结合

使用 OpenAI API

以下是与 OpenAI API 结合使用的示例,支持流式输出:

python
import os
import gradio as gr
from openai import OpenAI

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

def predict(message, history):
    history_openai_format = []
    for human, assistant in history:
        history_openai_format.append({"role": "user", "content": human})
        history_openai_format.append({"role": "assistant", "content": assistant})
    history_openai_format.append({"role": "user", "content": message})
  
    response = client.chat.completions.create(
        model='gpt-3.5-turbo',
        messages=history_openai_format,
        temperature=1.0,
        stream=True
    )

    partial_message = ""
    for chunk in response:
        if chunk.choices[0].delta.content is not None:
            partial_message = partial_message + chunk.choices[0].delta.content
            yield partial_message

demo = gr.ChatInterface(predict, type="messages")

if __name__ == "__main__":
    demo.launch()

使用 Hugging Face 模型

以下是使用 Hugging Face 的开源 LLM 的示例:

python
import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
from threading import Thread

# 加载模型和分词器
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen-7B-Chat", 
    torch_dtype=torch.float16, 
    device_map="auto"
)

def predict(message, history):
    # 格式化历史记录和当前消息
    prompt = tokenizer.apply_chat_template(
        [{"role": "user" if i % 2 == 0 else "assistant", "content": m} 
         for i, m in enumerate([item for sublist in history for item in sublist] + [message])],
        tokenize=False,
        add_generation_prompt=True
    )
    
    # 对提示进行分词
    model_inputs = tokenizer([prompt], return_tensors="pt").to(model.device)
    
    # 设置流式传输
    streamer = TextIteratorStreamer(tokenizer, timeout=10., skip_prompt=True, skip_special_tokens=True)
    
    # 设置生成参数
    generate_kwargs = dict(
        model_inputs,
        streamer=streamer,
        max_new_tokens=1024,
        do_sample=True,
        top_p=0.95,
        top_k=50,
        temperature=0.7,
        num_beams=1,
    )
    
    # 在单独的线程中生成文本
    t = Thread(target=model.generate, kwargs=generate_kwargs)
    t.start()

    # 流式输出结果
    partial_message = ""
    for new_token in streamer:
        partial_message += new_token
        yield partial_message

demo = gr.ChatInterface(predict, type="messages")

if __name__ == "__main__":
    demo.launch()

结论

通过 Gradio 的 ChatInterface 类,您可以轻松创建功能丰富的聊天机器人应用程序。从简单的文本响应到多模态交互,从基本演示到复杂的 LLM 集成,ChatInterface 提供了灵活且用户友好的方式来展示您的聊天模型。

如果您需要更高级的定制,可以考虑使用 Gradio 的低级 Blocks API 构建聊天机器人 UI,这将在后续指南中详细介绍。